/**
 ******************************************************************************
 *
 * @file       telemetry.h
 * @author     The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
 * @addtogroup GCSPlugins GCS Plugins
 * @{
 * @addtogroup UAVTalkPlugin UAVTalk Plugin
 * @{
 * @brief The UAVTalk protocol plugin
 *****************************************************************************/
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef TELEMETRY_H
#define TELEMETRY_H

#include "uavtalk.h"
#include "uavobjectmanager.h"
#include "gcstelemetrystats.h"
#include <QMutex>
#include <QMutexLocker>
#include <QTimer>
#include <QQueue>
#include <QMap>

class ObjectTransactionInfo : public QObject {
    Q_OBJECT

public:
    ObjectTransactionInfo(QObject *parent);
    ~ObjectTransactionInfo();
    UAVObject *obj;
    bool allInstances;
    bool objRequest;
    qint32 retriesRemaining;
    bool acked;
    QPointer<class Telemetry>telem;
    QTimer *timer;
private slots:
    void timeout();
};

class Telemetry : public QObject {
    Q_OBJECT

public:
    typedef struct {
        quint32 txBytes;
        quint32 txObjectBytes;
        quint32 txObjects;
        quint32 txErrors;
        quint32 txRetries;

        quint32 rxBytes;
        quint32 rxObjectBytes;
        quint32 rxObjects;
        quint32 rxErrors;
        quint32 rxSyncErrors;
        quint32 rxCrcErrors;
    } TelemetryStats;

    Telemetry(UAVTalk *utalk, UAVObjectManager *objMngr);
    ~Telemetry();
    TelemetryStats getStats();
    void resetStats();
    void transactionTimeout(ObjectTransactionInfo *info);

private:
    // Constants
    static const int REQ_TIMEOUT_MS = 250;
    static const int MAX_RETRIES    = 2;
    static const int MAX_UPDATE_PERIOD_MS = 1000;
    static const int MIN_UPDATE_PERIOD_MS = 1;
    static const int MAX_QUEUE_SIZE = 20;

    // Types
    /**
     * Events generated by objects
     */
    typedef enum {
        EV_NONE             = 0x00, /** No event */
        EV_UNPACKED         = 0x01, /** Object data updated by unpacking */
        EV_UPDATED          = 0x02, /** Object data updated by changing the data structure */
        EV_UPDATED_MANUAL   = 0x04, /** Object update event manually generated */
        EV_UPDATED_PERIODIC = 0x08, /** Object update event generated by timer */
        EV_UPDATE_REQ       = 0x10 /** Request to update object data */
    } EventMask;

    typedef struct {
        UAVObject *obj;
        qint32    updatePeriodMs; /** Update period in ms or 0 if no periodic updates are needed */
        qint32    timeToNextUpdateMs; /** Time delay to the next update */
    } ObjectTimeInfo;

    typedef struct {
        UAVObject *obj;
        EventMask event;
        bool allInstances;
    } ObjectQueueInfo;

    // Variables
    UAVObjectManager *objMngr;
    UAVTalk *utalk;
    GCSTelemetryStats *gcsStatsObj;
    QList<ObjectTimeInfo> objList;
    QQueue<ObjectQueueInfo> objQueue;
    QQueue<ObjectQueueInfo> objPriorityQueue;
    QMap<quint32, QMap<quint32, ObjectTransactionInfo *> *> transMap;
    QMutex *mutex;
    QTimer *updateTimer;
    QTimer *statsTimer;
    qint32 timeToNextUpdateMs;
    quint32 txErrors;
    quint32 txRetries;

    // Methods
    void registerObject(UAVObject *obj);
    void addObject(UAVObject *obj);
    void setUpdatePeriod(UAVObject *obj, qint32 periodMs);
    void connectToObjectInstances(UAVObject *obj, quint32 eventMask);
    void connectToObject(UAVObject *obj, quint32 eventMask);
    void updateObject(UAVObject *obj, quint32 eventMask);
    void processObjectUpdates(UAVObject *obj, EventMask event, bool allInstances, bool priority);
    void processObjectTransaction(ObjectTransactionInfo *transInfo);
    void processObjectQueue();

    ObjectTransactionInfo *findTransaction(UAVObject *obj);
    void openTransaction(ObjectTransactionInfo *trans);
    void closeTransaction(ObjectTransactionInfo *trans);
    void closeAllTransactions();

private slots:
    void objectUpdatedAuto(UAVObject *obj);
    void objectUpdatedManual(UAVObject *obj, bool all = false);
    void objectUpdatedPeriodic(UAVObject *obj);
    void objectUnpacked(UAVObject *obj);
    void updateRequested(UAVObject *obj, bool all = false);
    void newObject(UAVObject *obj);
    void newInstance(UAVObject *obj);
    void processPeriodicUpdates();
    void transactionCompleted(UAVObject *obj, bool success);
};

#endif // TELEMETRY_H
